package network.networkManager;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import network.Network;
import network.NetworkEvent;
import network.NetworkListener;
import network.interpreter.Interpreter;

/**
 * manages the network, interprets and processes all incomming data,
 * this class is meant to act as a client or server depending on configuation
 * <br><br>
 * note: this class is meant to be extended in order to be utilized (ie, by
 * server and client classes)
 * @author jack
 *
 */
public abstract class NetworkManager implements NetworkListener
{
	/**
	 * the port the server listens on, this should be passed for the
	 * constructor of the game server (when extending this class)
	 */
	public static final int serverPort = 33856;
	/**
	 * the port the client listens on, the should be passed for the
	 * constructor of the client server (when extending this class)
	 */
	public static final int clientPort = 33857;
	
	private Network n;
	/**
	 * contains the mapping of registered interpreters by ID, must be synchronized in order to avoid
	 * issues of registering interpreters while receiving packets
	 */
	private Map<Byte, Interpreter> interpreters = Collections.synchronizedMap(new HashMap<Byte, Interpreter>());
	
	/**
	 * creates a new network manager
	 * <br><br>
	 * note: because this class makes use of Network, it will spawn 2 threads upon instantiation,
	 * see {@link network.Network} for more information
	 * @param listeningPort the port the network manager should listen on for incomming packets
	 * @throws IOException
	 */
	protected NetworkManager(int listeningPort) throws IOException
	{
		n = new Network(listeningPort);
	}
	/**
	 * gets a reference to the network used by this manager, should be used for
	 * registering network profiles
	 * @return returns a network reference
	 */
	protected Network getNetwork()
	{
		return n;
	}
	/**
	 * registers an interpreter with this network manager, when the network
	 * manager receives packet information it will lookup an interpreter for
	 * each data element contained within the packet from its mapping of
	 * registered interpreters
	 * @param i the interpreter to be registered
	 */
	protected void registerInterpreter(Interpreter i)
	{
		try
		{
			if(interpreters.containsKey(i.getID()))
				throw new Exception("interpreter ID conflict! ID="+i.getID()+
						" is already used by "+interpreters.get(i.getID()).getClass().getName());
			interpreters.put(i.getID(), i);
			System.out.println("interpreter "+i.getClass().getName()+" registered, ID="+i.getID());
		}
		catch(Exception e)
		{
			System.out.println("interpreter "+i.getClass().getName()+" with ID="+i.getID()+" NOT registered");
		}
	}
	/**
	 * parses the received packets and automatically sends the data to the appropriate interpreter
	 * <br><br>
	 * note: this method can be manipulated to simulate packet receives for testing purposes
	 */
	public void packetReceived(NetworkEvent e)
	{
		ByteBuffer b = e.getPacketData();
		while(b.hasRemaining())
		{
			byte id = b.get(); //the interpreter id to use
			int dataLength = b.get()-Byte.MIN_VALUE;
			byte[] data = new byte[dataLength];
			b.get(data);
			interpreters.get(id).interpret(data, e.getInetAddress());
		}
	}
}
